﻿#include "lang_impl.hh"
#include "util/string_util.hh"
#include "box/service_box.hh"
#include <fstream>

namespace kratos {
namespace lang {

#define LANG_DEFINE(name, s)\
   {std::underlying_type<LangID>::type(LangID::name), {s, ""} },

#define LANG_DEFINE_TOKEN(name, s, t)\
    {std::underlying_type<LangID>::type(LangID::name), {s, t} },

static DefaultMap global_default_lang = {
    LANG_DEFINE(LANG_BOX_PARSE_ARGUMENT_FAIL, "[box]Runtime argument invalid")
    LANG_DEFINE_TOKEN(LANG_BOX_LOAD_CONFIG_FAIL, "[box][config]Load configuration file error[%s]", "%s")
    LANG_DEFINE(LANG_BOX_OS_REGISTER_FAIL, "[box]Register as OS service failed")
    LANG_DEFINE(LANG_BOX_START_NETWORK_FAIL, "[box]Start networking component failed")
    LANG_DEFINE(LANG_BOX_GETTING_NECESSARY_SERVICE, "[box]Querying necessary services...")
    LANG_DEFINE(LANG_BOX_GOT_NECESSARY_SERVICE, "[box]Querying necessary services...done")
    LANG_DEFINE(LANG_BOX_STARTED, "[box]Start box successfully")
    LANG_DEFINE(LANG_BOX_START_FAILURE, "[box]Start box failed")
    LANG_DEFINE_TOKEN(LANG_BOX_PARSE_CONFIG_FAIL, "[box]Configuration file error[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_BOX_CREATE_FINDER_FAIL, "[box]Service finder component error, finder type[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_BOX_START_FINDER_FAIL, "[box]Service register component error, register type[%s], register address[%s]", "%s %s")
    LANG_DEFINE(LANG_BOX_START_CLEANUP, "[box]Cleaning up box...")
    LANG_DEFINE(LANG_BOX_STOPPED, "[box]Cleaning up box...done")
    LANG_DEFINE_TOKEN(LANG_BOX_SERVICE_ADDRESS_INCORRECT, "[box]Service address format error[%s], service name[%s]", "%s %s")
    LANG_DEFINE(LANG_BOX_CREATE_LOGGER_FAIL, "[box]Create logger component failed")
    LANG_DEFINE_TOKEN(LANG_BOX_START_LOGGER_FAIL, "[box]Start logger appender failed, configuration[%s], error[%s]", "%s %s")
    LANG_DEFINE_TOKEN(LANG_BOX_LOCAL_SERVICE_DIR_NOT_FOUND, "[box]The directory of service is unexisted[%s]", "%s")
    LANG_DEFINE(LANG_BOX_START_REMOTE_SERVICE_FAIL, "[box]Http loader start failed")
    LANG_DEFINE_TOKEN(LANG_BOX_LOAD_LOCAL_SERVICE_FAIL, "[box]Load service[%s] failed, path[%s], UUID[%s]", "%s %s %s")
    LANG_DEFINE_TOKEN(LANG_BOX_LOAD_LOCAL_SERVICE_DIR_NOT_FOUND, "[box]Load service[%s] failed [path not found], path[%s], UUID[%s]", "%s %s %s")
    LANG_DEFINE_TOKEN(LANG_BOX_LOAD_LOCAL_SERVICE_SUCCESS, "[box]Start service[%s] successfully", "%s")
    LANG_DEFINE_TOKEN(LANG_BOX_START_LISTENER_FAIL, "[box]Start network listener failed, address[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_OS_RELOAD_CONFIG_FAIL, "[box]Reload configuration failed[%s], error[%s]", "%s %s")
    LANG_DEFINE_TOKEN(LANG_OS_RELOAD_CONFIG_SUCCESS, "[box]Reload configuration successfully[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_CONNECT_REMOTE_REPO_FAIL, "[box]Call remote repo API failed, API[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_PARSE_REMOTE_VERSION_CONFIG_FAIL, "[box]Parse remote version file failed, API[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_CREATE_LOCAL_FILE_FAIL, "[box]Create file failed, API[%s], path[%s]", "%s %s")
    LANG_DEFINE_TOKEN(LANG_HTTP_CREATE_LOCAL_DIR_FAIL, "[box]Create TMP directory failed, path[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_DOWNLOAD_REMOTE_BUNDLE_FAIL, "[box]Download remote service failed, API[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_UPDATE_REMOTE_SERVICE_EXCEPT, "[box]Unexpected exception when update remote service[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_UPDATE_SERVICE_FAIL, "[box]Update service failed, new service[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_CREATE_LOCAL_VERSION_FILE_FAIL, "[box]Create VERSION fail failed, version[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_CREATE_CONNECTOR_FAIL, "[box]Start HTTP connector failed[%s:%d]", "%s %d")
    LANG_DEFINE_TOKEN(LANG_HTTP_CREATE_LISTENER_FAIL, "[box]Start HTTP listener failed[%s:%d]", "%s %d")
    LANG_DEFINE(LANG_SERVICE_CONTEXT_REQUEST_SHUTDOWN, "[box]Request shutdown...")
    LANG_DEFINE_TOKEN(LANG_CORO_RUNNER_EXCEPTION, "[box]Coroutine throw exception, reason[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_COMMAND_EXCEPTION, "[box]Unexpected exception when EXEC command, command[%s], exception[%s]", "%s %s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_LISTENER,"[box]Listening[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_FINDER, "[box]Service finder type[%s], address[%s]", "%s %s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_NECESSARY_SERVICE, "[box]Necessary service[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_REGISTER_SERVICE, "[box]Register service[%s], Listen at[%s]", "%s %s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_UNREGISTER_SERVICE, "[box]Cancel service registering[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_COROUTINE, "[box]Start coroutine mode[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_LOCAL_SERVICE_DIR, "[box]Service directory[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_LOCAL_SERVICE, "[box]Preload service[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_OPEN_REMOTE_UPDATE, "[box]Switch on remote update[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_REMOTE_REPO_DIR, "[box]Remote repo directory[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_REMOTE_VERSION_API, "[box]Remote repo version API[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_REMOTE_LATEST_VRSION_API, "[box]Remote repo latest verison API[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_REMOTE_UPDATE_CHECK_INTVAL, "[box]Remote update check interval[%d(sec)]", "%d")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_DAEMON, "[box]Switch on daemon mode[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_COMMAND_MANAGER, "[box]Switch on command manager[%s]","%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_COMMAND_MANAGER_ADDRESS, "[box]Command manager listen at[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_COMMAND_INFO, "[box]Execute command[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_RELOAD_INFO, "[box]Reload configuration, OLD[%s], NEW[%s]", "%s %s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_EXCEPTION, "[box]Unexpected exception[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_RUNTIME_EXCEPTION, "[box]Unexpected runtime exception, exception[%s][%s], more exception may throw", "%s %s")
    LANG_DEFINE_TOKEN(LANG_START_LISTENER_FAIL, "[box]Start network listener failed[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_CONFIG_ERROR, "[box]Configuration error, attribute[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_UNEXPECTED_EXCEPTION, "[box][%s]Unexpected exception, [%s][%s]", "%s %s %s")
    LANG_DEFINE_TOKEN(LANG_UNEXPECTED_ERROR, "[box][%s]Unexpected error[%s]", "%s %s")
    LANG_DEFINE(LANG_PROXY_CONFIG_NOT_FOUND, "[box][proxy]Configuration failed, attribute [proxy.listener] not found")
    LANG_DEFINE(LANG_START_AS_PROXY, "[box][proxy]Start as proxy")
    LANG_DEFINE(LANG_PROXY_SEED_NOT_FOUND, "[box][proxy]Configuration failed, attribute [proxy.seed] not found")
    LANG_DEFINE_TOKEN(LANG_REGISTER_UUID_SERVICE_FAILED, "[box]Register service failed, UUID[%s] service[%s]", "%s %s")
    LANG_DEFINE(LANG_PROXY_VIRTUAL_ID_EXHAUSTED, "[box][proxy]Proxy ID run out")
    LANG_DEFINE(LANG_PROXY_ADD_OUTSIDE_TRANSPORT_FAILED, "[box][proxy]Add external connection failed")
    LANG_DEFINE_TOKEN(LANG_PROXY_FIND_SERVICE_TIMEOUT, "[box][proxy]Query service timeout，UUID[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_PROXY_INVALID_HEADER, "[box][proxy]Invalid protocol header, header[%s]", "%s")
    LANG_DEFINE(LANG_PROXY_NOT_FOUND_SERVICE_FOR_RETURN, "[box][proxy]Proxy return but not found caller")
    LANG_DEFINE_TOKEN(LANG_PROXY_CONFIG_ERROR, "[box][proxy]Configuration error[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_PROXY_LISTENER_INFO, "[box][proxy]Proxy listener[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_REDIS_INVALID_ARGUMENT, "[box][redis]Argument error[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_REDIS_CONNECT_FAIL, "[box][redis]Connected redis service failed[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_REDIS_EXEC_FAIL, "[box][redis][worker]Execute command failed[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_HOST_CONNECT_INFO, "[box][service_layer]Remote box connected, service[%s], address[%s], service reference[%d]", "%s %s %d")
    LANG_DEFINE_TOKEN(LANG_HOST_DISCONNECT_INFO, "[box][service_layer]Remote box disconnected, service[%s], address[%s], service reference[%d]", "%s %s %d")
    LANG_DEFINE_TOKEN(LANG_TIMER_HANDLE_EXCEPTION, "[box][timer]Unexpected exception when handling timer, reason[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_CSV_REBUILD_INDEX_FAIL, "[box][csv]Reindex failed after reload configuration, file[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_CONFIG_TYPE_ERROR, "[box][config]Configuration type error, attribute[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_CONFIG_MISSING_ATTR, "[box][config]Configuration attribute not found, attribute[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_ASSERTION_FAIL, "[box]Assert failure[%s], location[%s:%d]:\nstack:%s", "%s %s %d %s")
    LANG_DEFINE_TOKEN(LANG_SERVICE_ASSERTION_FAIL, "Service assert failure[%s], location[%s:%d]:\nstack:%s", "%s %s %d %s")
    LANG_DEFINE(LANG_CONSOLE_CONFIG_ERROR, "[box][console]Configuration error, HTML page not found")
    LANG_DEFINE_TOKEN(LANG_LUA_ERROR, "[box][lua]%s", "%s")
    LANG_DEFINE(LANG_ANOTHER_INSTANCE, "[box]Another instance was running")
    LANG_DEFINE_TOKEN(LANG_CWD, "[box]Current working directory[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_PID, "[box]PID[%d]", "%d")
    LANG_DEFINE_TOKEN(LANG_BIN_NAME, "[box]Executable binary[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_BIN_PATH, "[box]Executable binary path[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_LOAD_MODULE_ERROR, "[box][module]Load module error[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_LIMITER_ERROR, "[box][limiter]Limiter error[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_LOAD_MODULE_SUCCESS, "[box][module]Load module[%s] successfully", "%s")
    LANG_DEFINE_TOKEN(LANG_INFO, "[box][%s]%s", "%s %s")
};

kratos::lang::LangImpl::LangImpl(bool internal) {
  if (internal) {
    load_default("cn-simple", global_default_lang);
  }
}

kratos::lang::LangImpl::~LangImpl() {}

auto kratos::lang::LangImpl::load_default(const std::string &default_lang_path)
    -> bool {
  default_lang_map_.clear();
  if (!load(default_lang_path)) {
    return false;
  }
  default_lang_map_.swap(lang_map_);
  language_.clear();
  return true;
}

auto kratos::lang::LangImpl::load_default(const std::string& name,
                                          const DefaultMap &default_map)
    -> void {
  default_language_ = name;
  for (const auto& [k, v] : default_map) {
      default_lang_map_[k] = v;
  }
}

auto kratos::lang::LangImpl::get_language() -> const std::string & {
  if (language_.empty()) {
    return default_language_;
  }
  return language_;
}

auto kratos::lang::LangImpl::get_lang(kratos::lang::LangID id) noexcept
    -> const std::string & {
  static std::string null;
  auto it = lang_map_.find(static_cast<std::uint64_t>(id));
  if (it == lang_map_.end()) {
    auto it_default = default_lang_map_.find(static_cast<std::uint64_t>(id));
    if (it_default != default_lang_map_.end()) {
      return it_default->second.line;
    } else {
      return null;
    }
  }
  return it->second.line;
}

auto kratos::lang::LangImpl::load(const std::string &file_path) -> bool {
  std::ifstream ifs;
  ifs.open(file_path, std::ios::in);
  if (!ifs) {
    return false;
  }
  std::string line;
  std::getline(ifs, line);
  std::vector<std::string> result;
  util::split(line, " ", result);
  if (result.size() != 2) {
    return false;
  }
  std::string language = result[1];
  std::vector<std::string> token_result;
  while (std::getline(ifs, line)) {
    if (line.empty()) {
      continue;
    }
    std::string log_line;
    util::splitBy(line, ' ', result);
    if (result.size() < 2) {
      lang_map_.clear();
      return false;
    }
    if (result.size() >= 2) {
      for (std::size_t i = 1; i < result.size(); i++) {
        log_line += result[i];
      }
    }
    std::uint64_t lang_id = std::stoull(result[0]);
    lang_map_[lang_id] = { log_line, "" };
    auto pos = log_line.find('%');
    auto it = global_default_lang.find(lang_id);
    if (it == global_default_lang.end()) {
      return false;
    }
    if (it->second.token.empty()) {
      if (pos != std::string::npos) {
        return false;
      }
    } else {
      util::split(it->second.token, " ", token_result);
      std::size_t token_pos = 0;
      for (const auto& token : token_result) {
        auto n = log_line.find(token, token_pos);
        if (n == std::string::npos) {
          return false;
        }
        token_pos = n;
      }
      if (token_pos + 1 < log_line.size()) {
        if (std::string::npos != log_line.find('%', token_pos + 1)) {
          return false;
        }
      }
    }
  }
  file_path_ = file_path;
  language_ = language;
  return true;
}

auto kratos::lang::LangImpl::reload() -> bool {
  std::string file_path = file_path_;
  lang_map_.clear();
  return load(file_path);
}

} // namespace lang
} // namespace kratos


